﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DirectoryTree_Scanning
{
    #region event stuff
    //delegates
    public delegate void BeforeProcessingEventHandler(object sender);
    public delegate void AfterProcessingEventHandler(object sender);
    public delegate void ProcessingFileEventHandler(object sender, ProcessingFileEventArgs e);
    public delegate void ProcessingDirectoryEventHandler(object sender, ProcessingDirectoryEventArgs e);

    //event arg classes
    public class ProcessingFileEventArgs
    {
        public string ProcessedFile { get; set; }
        public long FileSize { get; set; }
    }

    public class ProcessingDirectoryEventArgs
    {
        public string ProcessedDirectory { get; set; }
    }
    #endregion



    class DirectorySync
    {
        #region declarations
        private Thread _thrScanThread;

        #region events
        //events
        public event BeforeProcessingEventHandler BeforeProcessing;
        public event AfterProcessingEventHandler AfterProcessing;
        public event ProcessingFileEventHandler ProcessingFile;
        public event ProcessingDirectoryEventHandler ProcessingDirectory;

        //event callers
        protected void OnBeforeProcessing()
        {
            if (BeforeProcessing != null)
            {
                BeforeProcessing(this);
            }
        }

        protected void OnAfterProcessing()
        {
            if (AfterProcessing != null)
            {
                AfterProcessing(this);
            }
        }

        protected void OnProcessingFile(
            ProcessingFileEventArgs ea)
        {
            if (ProcessingFile != null)
            {
                ProcessingFile(this, ea);
            }
        }

        protected void OnProcessingDirectory(
            ProcessingDirectoryEventArgs ea)
        {
            if (ProcessingDirectory != null)
            {
                ProcessingDirectory(this, ea);
            }
        }
        #endregion
        #endregion



        #region constructor
        public DirectorySync()
        {
            _thrScanThread = new Thread(_scanDirectoryTreeAsync);
        }
        #endregion



        #region private functions
        private void _scanDirectoryTree(
            object data)
        {
            //first check break condition
            if (_fHalt) { return; }

            //get data
            ScanDirectoryConfig sdc = (ScanDirectoryConfig)data;

            //prepare resultsets
            string[] aryFileNames = Directory.GetFiles(sdc.ScanPath);
            string[] aryDirectoryNames = Directory.GetDirectories(sdc.ScanPath);

            //cycle through files
            foreach (string FileName in aryFileNames)
            {
                ProcessingFileEventArgs fea = new ProcessingFileEventArgs() { FileSize = 0, ProcessedFile = "" };

                if ((sdc.Type & ScanDirectoryTypes.SumFileSize) == ScanDirectoryTypes.SumFileSize)
                {
                    //process file
                    FileInfo fi = new FileInfo(FileName);
                    fea.FileSize = fi.Length;
                }

                //increase count and throw event
                OnProcessingFile(fea);
            }

            //cycle through directories
            foreach (string DirectoryName in aryDirectoryNames)
            {
                //increase count and throw event
                OnProcessingDirectory(new ProcessingDirectoryEventArgs() { ProcessedDirectory = DirectoryName });

                //process directory
                sdc.ScanPath = DirectoryName;
                _scanDirectoryTree(sdc);
            }
        }

        private void _scanDirectoryTreeAsync(
            object data)
        {
            OnBeforeProcessing();

            _scanDirectoryTree(data);
            if (this.Halt) { this.Halt = false; }

            OnAfterProcessing();
        }
        #endregion



        #region methods
        public void ScanDirectoryTree(
            ScanDirectoryConfig sdc)
        {
            _scanDirectoryTree(sdc);
        }
        public void ScanDirectoryTreeAsync(
            ScanDirectoryConfig sdc)
        {
            //normal thread
            if (_thrScanThread == null ||
                _thrScanThread.ThreadState == ThreadState.Stopped ||
                _thrScanThread.ThreadState == ThreadState.Aborted)
            {
                _thrScanThread = new Thread(_scanDirectoryTreeAsync);
                _thrScanThread.Start(sdc);
            }
        }
        public void ScanDirectoryTreeAsyncThreadPool(
            ScanDirectoryConfig sdc)
        {
            //alternative ThreadPool
            ThreadPool.QueueUserWorkItem(o => _scanDirectoryTreeAsync(sdc));
        }
        #endregion



    
        #region properties
        public Thread ScanThread
        {
            get
            {
                return _thrScanThread;
            }
        }

        /// <summary>
        /// stops the thread by breaking out of all recursions.
        /// </summary>
        private volatile bool _fHalt = false;
        public bool Halt
        {
            get
            {
                return _fHalt;
            }
            set
            {
                _fHalt = value;
            }
        }
        #endregion
    }



    class ScanDirectoryConfig
    {
        public string ScanPath { get; set; }
        public ScanDirectoryTypes Type { get; set; }
    }



    [Flags]
    enum ScanDirectoryTypes
    {
        Count = 0x0,
        SumFileSize = 0x1,
    }
}
